home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / cmds / oldwish / wishCmd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-02-27  |  54.0 KB  |  2,091 lines

  1. /* 
  2.  * wishCmd.c --
  3.  *
  4.  *    Commands for wish.
  5.  *
  6.  * Copyright 1987 Regents of the University of California
  7.  * All rights reserved.
  8.  * Permission to use, copy, modify, and distribute this
  9.  * software and its documentation for any purpose and without
  10.  * fee is hereby granted, provided that the above copyright
  11.  * notice appear in all copies.  The University of California
  12.  * makes no representations about the suitability of this
  13.  * software for any purpose.  It is provided "as is" without
  14.  * express or implied warranty.
  15.  */
  16.  
  17. #ifndef lint
  18. static char rcsid[] = "$Header: /a/newcmds/wish/RCS/wishCmd.c,v 1.5 89/01/19 16:51:50 mgbaker Exp Locker: mgbaker $ SPRITE (Berkeley)";
  19. #endif not lint
  20.  
  21. #include <sys/types.h>
  22. #include <sys/stat.h>
  23. #include "sx.h"
  24. #include "util.h"
  25. typedef    int    Boolean;
  26. #define    FALSE    0
  27. #define    TRUE    1
  28. #include "monitorClient.h"
  29. #include "wishInt.h"
  30.  
  31. /*
  32.  * Range of chars for which the standard insert binding is to be used,
  33.  * unless overridden by something else.
  34.  */
  35. static    char    insertFirst = 040;
  36. static    char    insertLast = 0176;
  37.  
  38.  
  39.  
  40. /*
  41.  *----------------------------------------------------------------------
  42.  *
  43.  * WishBindCmd --
  44.  *
  45.  *    Create (or replace or delete) a keystroke binding.  I should change
  46.  *    this to match mx so that a missing command argument means to return
  47.  *    the enumeration of the bindings with the prefix, rather than to
  48.  *    delete the binding.
  49.  *
  50.  * Syntax:
  51.  *    bind sequence [command]
  52.  *
  53.  * Results:
  54.  *    Returns TCL results.
  55.  *
  56.  * Side effects:
  57.  *    A new keystroke binding gets added to or deleted from the
  58.  *    window's command table.
  59.  *
  60.  *----------------------------------------------------------------------
  61.  */
  62. int
  63. WishBindCmd(aWindow, interp, argc, argv)
  64.     WishWindow    *aWindow;
  65.     Tcl_Interp        *interp;
  66.     int            argc;
  67.     char        **argv;
  68. {
  69.     if ((argc != 3) && (argc != 2)) {
  70.     sprintf(interp->result, "%s should be \"%.50s [sequence [command]]\"",
  71.         "wrong number of args:", argv[0]);
  72.     return TCL_ERROR;
  73.     }
  74.     if (argc == 3) {
  75.     Cmd_BindingCreate(aWindow->cmdTable, argv[1], argv[2]);
  76.     } else {
  77.     Cmd_BindingDelete(aWindow->cmdTable, argv[1]);
  78.     }
  79.     return TCL_OK;
  80. }
  81.  
  82.  
  83. /*
  84.  *----------------------------------------------------------------------
  85.  *
  86.  * WishChangeDirCmd --
  87.  *
  88.  *    Change the directory of the display.  Force associated shell
  89.  *    window(s) to update current directory also.
  90.  *
  91.  * Syntax:
  92.  *    changeDirectory dirName
  93.  *
  94.  * Results:
  95.  *    Returns TCL_OK if all went well, or various TCL errors if not.
  96.  *
  97.  * Side effects:
  98.  *    Current directory changes.
  99.  *
  100.  *----------------------------------------------------------------------
  101.  */
  102. int
  103. WishChangeDirCmd(aWindow, interp, argc, argv)
  104.     WishWindow    *aWindow;
  105.     Tcl_Interp        *interp;
  106.     int            argc;
  107.     char        **argv;
  108. {
  109.     struct    stat    dirAtts;
  110.     char        *newDir;
  111.  
  112.     if (argc != 2) {
  113.     sprintf(interp->result, "%s \"%.50s dirName\"",
  114.         "wrong # args: should be", argv[0]);
  115.     return TCL_ERROR;
  116.     }
  117.     if ((newDir = Util_CanonicalDir(argv[1], aWindow->dir, (char *) NULL))
  118.         == NULL) {
  119.     sprintf(interp->result,
  120.         "Couldn't figure out directory name %s.", argv[1]);
  121.     return TCL_ERROR;
  122.     }
  123.     /* Check the validity of the new dir. */
  124.     if (lstat(newDir, &dirAtts) != 0) {
  125.     sprintf(interp->result,
  126.         "Cannot switch to dir %s.  Maybe it doesn't exist?", newDir);
  127.     free(newDir);
  128.     return TCL_ERROR;    /* should above message be passed to routine? */
  129.     }
  130.     if ((dirAtts.st_mode & S_IFMT) != S_IFDIR) {    /* not a directory */
  131.     sprintf(interp->result, "%s is not a directory.", newDir);
  132.     free(newDir);
  133.     return TCL_ERROR;    /* should above message be passed to routine? */
  134.     }
  135.     WishChangeDir(aWindow, newDir);    /* does everything */
  136.     free(newDir);
  137.  
  138.     return TCL_OK;
  139. }
  140.  
  141.  
  142. /*
  143.  *----------------------------------------------------------------------
  144.  *
  145.  * WishChangeFieldsCmd --
  146.  *
  147.  *    Change the fields displayed with each file.
  148.  *
  149.  * Syntax:
  150.  *    changeFields [fields]
  151.  *
  152.  * Results:
  153.  *    Returns TCL_OK if all went well, or TCL errors if not.
  154.  *
  155.  * Side effects:
  156.  *    The fields displayed for each file will change.  The user will be
  157.  *    prompted to see if he wishes to make the new sorting method the
  158.  *    default method for the directory.
  159.  *
  160.  *----------------------------------------------------------------------
  161.  */
  162. int
  163. WishChangeFieldsCmd(aWindow, interp, argc, argv)
  164.     WishWindow    *aWindow;
  165.     Tcl_Interp        *interp;
  166.     int            argc;
  167.     char        **argv;
  168. {
  169.     int    which;
  170.     int    length;
  171.     int    arg;
  172.  
  173.     /*
  174.      * any number of args may be okay here, depending on how many fields they
  175.      * wish to display.
  176.      */
  177.     aWindow->displayInstructions = 0;
  178.     if (argc > 1) {
  179.     for (arg = 1; arg < argc; arg++) {
  180.         length = strlen(argv[arg]);
  181.         if (strncmp(argv[arg], "name", length) == 0 ||
  182.             strncmp(argv[arg], "Name", length) == 0) { /* default */
  183.         continue;
  184.         }
  185.         /*
  186.          * atime = access time = time file data last read or modified.
  187.          * ctime = desc modify time = time file status last changed, by
  188.          *        writing or inode changes.
  189.          * mtime = data modify time = time data last modified.
  190.          */
  191.  
  192.         if (strncmp(argv[arg], "size", length) == 0 ||
  193.             strncmp(argv[arg], "Size", length) == 0) {
  194.         aWindow->displayInstructions |= WISH_SIZE_FIELD;
  195.         continue;
  196.         }
  197.         if (strncmp(argv[arg], "atime", length) == 0 ||
  198.             strncmp(argv[arg], "Atime", length) == 0 ||
  199.             strncmp(argv[arg], "accesstime", length) == 0 ||
  200.             strncmp(argv[arg], "accessTime", length) == 0 ||
  201.             strncmp(argv[arg], "AccessTime", length) == 0) {
  202.         aWindow->displayInstructions |= WISH_ATIME_FIELD;
  203.         continue;
  204.         }
  205.         if (strncmp(argv[arg], "ctime", length) == 0 ||
  206.             strncmp(argv[arg], "Ctime", length) == 0 ||
  207.             strncmp(argv[arg], "descmodtime", length) == 0 ||
  208.             strncmp(argv[arg], "descModTime", length) == 0 ||
  209.             strncmp(argv[arg], "DescModTime", length) == 0 ||
  210.             strncmp(argv[arg], "descmodifytime", length) == 0 ||
  211.             strncmp(argv[arg], "descModifyTime", length) == 0 ||
  212.             strncmp(argv[arg], "DescModifyTime", length) == 0 ||
  213.             strncmp(argv[arg], "descriptormodifytime", length) == 0 ||
  214.             strncmp(argv[arg], "descriptorModifyTime", length) == 0 ||
  215.             strncmp(argv[arg], "DescriptorModifyTime", length) == 0) {
  216.         aWindow->displayInstructions |= WISH_DTIME_FIELD;
  217.         continue;
  218.         }
  219.         if (strncmp(argv[arg], "mtime", length) == 0 ||
  220.             strncmp(argv[arg], "Mtime", length) == 0 ||
  221.             strncmp(argv[arg], "datamodtime", length) == 0 ||
  222.             strncmp(argv[arg], "dataModTime", length) == 0 ||
  223.             strncmp(argv[arg], "DataModTime", length) == 0 ||
  224.             strncmp(argv[arg], "datamodifytime", length) == 0 ||
  225.             strncmp(argv[arg], "dataModifyTime", length) == 0 ||
  226.             strncmp(argv[arg], "DataModifyTime", length) == 0) {
  227.         aWindow->displayInstructions |= WISH_MTIME_FIELD;
  228.         continue;
  229.         }
  230.         sprintf(interp->result, "%s %s %s", "bad argument, the possible",
  231.             "arguments are size, atime (AccessTime),",
  232.             "mtime (DataModifyTime), and dtime (DescriptorModifyTime)");
  233.         return TCL_ERROR;
  234.     }
  235.     goto finishedFields;
  236.     }
  237.  
  238.     /* Protect data structure from redraws initiated in Sx_Notify */
  239.     aWindow->notifierP = TRUE;
  240.     which = Sx_Notify(wishDisplay, aWindow->surroundingWindow, -1, -1, 0,
  241.         "Display Size?", NULL, TRUE, "Yes", "No", "Stop",
  242.         (char *) NULL);
  243.     if (which == 0) {
  244.     aWindow->displayInstructions |= WISH_SIZE_FIELD;
  245.     } else if (which == 2) {
  246.     goto finishedFields;
  247.     }
  248.     which = Sx_Notify(wishDisplay, aWindow->surroundingWindow, -1, -1, 0,
  249.         "Display AccessTime?", NULL, TRUE, "Yes", "No", "Stop",
  250.         (char *) NULL);
  251.     if (which == 0) {
  252.     aWindow->displayInstructions |= WISH_ATIME_FIELD;
  253.     } else if (which == 2) {
  254.     goto finishedFields;
  255.     }
  256.     which = Sx_Notify(wishDisplay, aWindow->surroundingWindow, -1, -1, 0,
  257.         "Display DataModifyTime?", NULL, TRUE, "Yes", "No", "Stop",
  258.         (char *) NULL);
  259.     if (which == 0) {
  260.     aWindow->displayInstructions |= WISH_MTIME_FIELD;
  261.     } else if (which == 2) {
  262.     goto finishedFields;
  263.     }
  264.     which = Sx_Notify(wishDisplay, aWindow->surroundingWindow, -1, -1, 0,
  265.         "Display DescriptorModifyTime?", NULL, TRUE, "Yes", "No",
  266.         "Stop", (char *) NULL);
  267.     if (which == 0) {
  268.     aWindow->displayInstructions |= WISH_DTIME_FIELD;
  269.     } else if (which == 2) {
  270.     goto finishedFields;
  271.     }
  272. finishedFields:
  273.     ;    /* nothing */
  274.  
  275.     /* Safe now. */
  276.     aWindow->notifierP = FALSE;
  277.  
  278.     if (aWindow->dontDisplayChangesP == FALSE) {
  279.     /* update display */
  280.     if (aWindow->firstElement == UNINITIALIZED) {
  281.         aWindow->firstElement = 1;
  282.     }
  283.     WishSetPositions(aWindow);
  284.     }
  285.  
  286.     return TCL_OK;
  287. }
  288.  
  289. #ifdef NOTDEF
  290.  
  291. /*
  292.  *----------------------------------------------------------------------
  293.  *
  294.  * WishChangeGroupsCmd --
  295.  *
  296.  *    Change the definition of a group.
  297.  *
  298.  * Syntax:
  299.  *    changeGroups
  300.  *
  301.  * Results:
  302.  *    Returns TCL_OK if all went well, or TCL errors if not.
  303.  *    error occurred.
  304.  *
  305.  * Side effects:
  306.  *    The pattern matching rules or associated command bindings for the
  307.  *    group may change.
  308.  *
  309.  *----------------------------------------------------------------------
  310.  */
  311. int
  312. WishChangeGroupsCmd(aWindow, interp, argc, argv)
  313.     WishWindow    *aWindow;
  314.     Tcl_Interp        *interp;
  315.     int            argc;
  316.     char        **argv;
  317. {
  318.     static    int    count = 0;
  319.     FILE    *stream;
  320.     int    pid;
  321.     char    buffer[MAXPATHLEN];
  322.     char    **args;
  323.     int    pidArray[1];
  324.     WishGroup    *grpPtr;
  325.     int    childPid;
  326.  
  327.     if (strcmp(argv[0], "deleteGroup") == 0 ||
  328.         strcmp(argv[0], "deletegroup") == 0) {
  329.     /* delete the group and clean up */
  330.     }
  331.     if (
  332.     /*
  333.      * open a file
  334.      */
  335.     Proc_GetIDs(&pid, NULL, NULL, NULL, NULL);
  336.     sprintf(buffer, "%s%d.%d", "/tmp/tmpWish", pid, count);
  337.     count++;
  338.     if ((stream = fopen(buffer, "w")) == NULL) {
  339.     sprintf(interp->result, "%s %s %s",
  340.         "Couldn't open file", buffer,
  341.         "in which to let you edit the selection rules.");
  342.     return TCL_ERROR;
  343.     }
  344.     for (grpPtr = aWindow->groupList; grpPtr != NULL;
  345.         grpPtr = grpPtr->nextPtr) {
  346.     fputs("Select:\n", stream);
  347.     fputs(grpPtr->rule, stream);
  348.     fputs("\n", stream);
  349.     if (grpPtr->name != NULL) {
  350.         fputs("GroupName:\n", stream);
  351.         fputs(grpPtr->name, stream);
  352.         fputs("\n\n", stream);
  353.     }
  354.     }
  355.     fclose(stream);
  356.     /* I should be checking ferror for above PutStrings and Close return. */
  357.     /* close file and mx it. */
  358.     /* parse file when mx returns. */
  359.     if (Proc_Fork(TRUE, &childPid) == PROC_CHILD_PROC) {
  360.     /*child*/
  361.     /* what to do about this pathname? */
  362.     args = (char **) malloc(4 * sizeof (char *));
  363.     args[0] = Util_Strcpy(NULL, "mx");
  364.     args[1] = Util_Strcpy(NULL, "-D");
  365.     args[2] = Util_Strcpy(NULL, buffer);    /* CORE LEAK */
  366.     args[3] = NULL;
  367.     Proc_Exec("/sprite2/users/ouster/mx10/mx", args, FALSE);
  368.     /* error if returned. */
  369.     sprintf(wishErrorMsg, "Exec of %s returned.",
  370.         "/sprite2/users/ouster/mx10/mx");
  371.     aWindow->notifierP = TRUE;
  372.     Sx_Notify(wishDisplay, aWindow->surroundingWindow, -1, -1, 0,
  373.         wishErrorMsg, NULL, TRUE, "Skip command", (char *) NULL);
  374.     aWindow->notifierP = FALSE;
  375.     exit(-1);
  376.     } else {
  377.     /*parent waits */
  378.     pidArray[0] = childPid;
  379.     Proc_Wait(1, pidArray, PROC_WAIT_BLOCK, NULL, NULL, NULL, NULL, NULL);
  380.     }
  381.     if ((stream = fopen(buffer, "a")) == NULL) {
  382.     sprintf(wishErrorMsg, "%s %s %s",
  383.         "Couldn't open file", buffer,
  384.         "in which you edited the selection rules.");
  385.     aWindow->notifierP = TRUE;
  386.     Sx_Notify(wishDisplay, aWindow->surroundingWindow, -1, -1, 0,
  387.         wishErrorMsg, NULL, TRUE, "Skip command", (char *) NULL);
  388.     aWindow->notifierP = FALSE;
  389.     return TCL_ERROR;
  390.     }
  391.     if (aWindow->sortingInstructions & WISH_ALPHA_SORT) {
  392.     if (aWindow->sortingInstructions & WISH_REVERSE_SORT) {
  393.         fputs("Sort:\nAlphaReverse\n\n", stream);
  394.     } else {
  395.         fputs("Sort:\nAlpha\n\n", stream);
  396.     }
  397.     }
  398.     if (aWindow->sortingInstructions & WISH_ATIME_SORT) {
  399.     if (aWindow->sortingInstructions & WISH_REVERSE_SORT) {
  400.         fputs("Sort:\nAccessTimeReverse\n\n", stream);
  401.     } else {
  402.         fputs("Sort:\nAccessTime\n\n", stream);
  403.     }
  404.     }
  405.     if (aWindow->sortingInstructions & WISH_MTIME_SORT) {
  406.     if (aWindow->sortingInstructions & WISH_REVERSE_SORT) {
  407.         fputs("Sort:\nDataModifyTimeReverse\n\n", stream);
  408.     } else {
  409.         fputs("Sort:\nDataModifyTime\n\n", stream);
  410.     }
  411.     }
  412.     if (aWindow->sortingInstructions & WISH_DTIME_SORT) {
  413.     if (aWindow->sortingInstructions & WISH_REVERSE_SORT) {
  414.         fputs("Sort:\nDescriptorModifyTimeReverse\n\n", stream);
  415.     } else {
  416.         fputs("Sort:\nDescriptorModifyTime\n\n", stream);
  417.     }
  418.     }
  419.     if (aWindow->sortingInstructions & WISH_SIZE_SORT) {
  420.     if (aWindow->sortingInstructions & WISH_REVERSE_SORT) {
  421.         fputs("Sort:\nSizeReverse\n\n", stream);
  422.     } else {
  423.         fputs("Sort:\nSize\n\n", stream);
  424.     }
  425.     }
  426.     if (aWindow->displayInstructions & WISH_NAME_FIELD) {
  427.     fputs("Display:\nName\n\n", stream);
  428.     }
  429.     if (aWindow->displayInstructions & WISH_ATIME_FIELD) {
  430.     fputs("Display:\nAccessTime\n\n", stream);
  431.     }
  432.     if (aWindow->displayInstructions & WISH_MTIME_FIELD) {
  433.     fputs("Display:\nDataModifyTime\n\n", stream);
  434.     }
  435.     if (aWindow->displayInstructions & WISH_DTIME_FIELD) {
  436.     fputs("Display:\nDescriptorModifyTime\n\n", stream);
  437.     }
  438.     if (aWindow->displayInstructions & WISH_SIZE_FIELD) {
  439.     fputs("Display:\nSize\n\n", stream);
  440.     }
  441.     fclose(stream);
  442.  
  443.     WishGarbageCollect(aWindow);
  444. #ifdef FUTURE
  445.     /*
  446.      * This routine was changed not to take second arg!
  447.      */
  448.     if (WishGatherNames(aWindow, buffer) != TCL_OK) {
  449.     return bad value and switch directories?
  450.     error string will be displayed in top-level display thingy?
  451.     }
  452. #endif FUTURE
  453.     
  454.     /* should it repick the size here if aWindow->pickSizeP is true? */
  455.     aWindow->firstElement = 1;
  456.     WishSetPositions(aWindow);
  457.     /* WishRedraw will be called from event caused in WishSetPositions() */
  458.  
  459.     /* ask if they want to make it permanent. */
  460.  
  461.     return TCL_OK;
  462. }
  463. #endif NOTDEF
  464.  
  465.  
  466.  
  467. /*
  468.  *----------------------------------------------------------------------
  469.  *
  470.  * WishChangeGroupCmd --
  471.  *
  472.  *    Change the definition of a group.
  473.  *
  474.  * Syntax:
  475.  *    changeGroup name newDefType newDef
  476.  *
  477.  * Results:
  478.  *    Returns TCL_OK if all went well, or TCL errors if not.
  479.  *
  480.  * Side effects:
  481.  *    The specified group will be changed.
  482.  *
  483.  *----------------------------------------------------------------------
  484.  */
  485. int
  486. WishChangeGroupCmd(aWindow, interp, argc, argv)
  487.     WishWindow    *aWindow;
  488.     Tcl_Interp        *interp;
  489.     int            argc;
  490.     char        **argv;
  491. {
  492.     WishGroup    *grpPtr;
  493.     WishGroup    *newGrpPtr;
  494.     WishGroup    *bPtr;
  495.  
  496.     if (argc != 4) {
  497.     sprintf(interp->result, "%s %s", "Wrong # of args. Must be 4 args,",
  498.         "\"changeGroup name newDefType newDef\"");
  499.     return TCL_ERROR;
  500.     }
  501.  
  502.     /* Make sure no other group already has the new definition. */
  503.     for (grpPtr = aWindow->groupList; grpPtr != NULL;
  504.         grpPtr = grpPtr->nextPtr) {
  505.     if (strcmp(argv[3], grpPtr->rule) == 0) {
  506.         if (grpPtr->fileList == NULL && aWindow->hideEmptyGroupsP) {
  507.             sprintf(interp->result, "%s %s, %s",
  508.             "A group already has the definition", argv[3],
  509.             "but it may not be visible since no files match it.");
  510.         } else {
  511.         sprintf(interp->result, "A group already has the definition %s",
  512.             argv[3]);
  513.         }
  514.         return TCL_ERROR;
  515.     }
  516.     }
  517.  
  518.     /* find group to replace */
  519.     if (aWindow->groupList == NULL) {
  520.     sprintf(interp->result,
  521.         "No groups have been defined in order to change one");
  522.     return TCL_ERROR;
  523.     }
  524.  
  525.     bPtr = NULL;
  526.     for (grpPtr = aWindow->groupList; grpPtr != NULL;
  527.         grpPtr = grpPtr->nextPtr) {
  528.     if (strcmp(argv[1], grpPtr->rule) == 0) {
  529.         break;
  530.     }
  531.     bPtr = grpPtr;
  532.     }
  533.  
  534.     if (grpPtr == NULL) {
  535.     sprintf(interp->result, "No group with that definition");
  536.     return TCL_ERROR;
  537.     }
  538.  
  539.  
  540.     newGrpPtr = (WishGroup *) malloc(sizeof (WishGroup));
  541.  
  542.     /* test the new group's definition */
  543.     if (GetNewGroup(aWindow, interp, argv + 2, newGrpPtr) != TCL_OK) {
  544.     free(newGrpPtr);
  545.     return TCL_ERROR;
  546.     }
  547.  
  548.     newGrpPtr->nextPtr = grpPtr->nextPtr;
  549.  
  550.     WishGarbageGroup(aWindow, grpPtr);
  551.  
  552.     if (bPtr == NULL) {
  553.     aWindow->groupList = newGrpPtr;
  554.     } else {
  555.     bPtr->nextPtr = newGrpPtr;
  556.     }
  557.     if (aWindow->dontDisplayChangesP == FALSE) {
  558.     /* update display */
  559.     if (aWindow->firstElement == UNINITIALIZED) {
  560.         aWindow->firstElement = 1;
  561.     }
  562.     WishSetPositions(aWindow);
  563.     }
  564.     
  565.     return TCL_OK;
  566. }
  567.  
  568.  
  569. /*
  570.  *----------------------------------------------------------------------
  571.  *
  572.  * GetNewGroup --
  573.  *
  574.  *    Initialize and collect files for a new group.
  575.  *
  576.  * Results:
  577.  *    Returns TCL_OK if all went well, or TCL errors if not.
  578.  *
  579.  * Side effects:
  580.  *    Memory will be allocated for various things.
  581.  *
  582.  *----------------------------------------------------------------------
  583.  */
  584. int
  585. GetNewGroup(aWindow, interp, argv, newGrpPtr)
  586.     WishWindow    *aWindow;
  587.     Tcl_Interp        *interp;
  588.     char        **argv;
  589.     WishGroup        *newGrpPtr;
  590. {
  591.     int        procArgc;
  592.     char    **procArgv;
  593.     int        num;
  594.  
  595.     /* parse command arguments */
  596.     if (strcmp("comparison", argv[0]) == 0) {    /* simple comp stuff */
  597.     newGrpPtr->defType = COMPARISON;
  598.     newGrpPtr->rule = Util_Strcpy((char *) NULL, argv[1]);
  599.     /* test new grp def */
  600.     if (Pattern_Match(newGrpPtr->rule, "x") < 0) {
  601.         sprintf(interp->result,
  602.             "The comparison rule {%s} contains an error",
  603.             newGrpPtr->rule);
  604.         free(newGrpPtr->rule);
  605.         return TCL_ERROR;
  606.     }
  607.     } else if (strcmp("proc", argv[0]) != 0) {    /* bad def */
  608.     sprintf(interp->result, "Bad group definition type %s", argv[0]);
  609.     return TCL_ERROR;
  610.     } else {        /* TCL proc */
  611.     newGrpPtr->defType = PROC;
  612.     /* get name of proc */
  613.     if (Tcl_SplitList(interp, argv[1], &procArgc, &procArgv) != TCL_OK) {
  614.         return TCL_ERROR;
  615.     }
  616.     newGrpPtr->rule = Util_Strcpy((char *) NULL, procArgv[1]);
  617.     if (Tcl_Eval(interp, argv[1], '\0', (char **) NULL) != TCL_OK) {
  618.         free(newGrpPtr->rule);
  619.         free(procArgv);
  620.         return TCL_ERROR;
  621.     }
  622.     /* Will this really free it?  Tcl man page says so... */
  623.     free(procArgv);
  624.  
  625.     /* test new grp def */
  626.     if (WishDoTclSelect(interp, newGrpPtr->rule, "x", &num) != TCL_OK) {
  627.         free(newGrpPtr->rule);
  628.         return TCL_ERROR;
  629.     }
  630.     }
  631.     newGrpPtr->nextPtr = NULL;
  632.     newGrpPtr->myColumn = UNINITIALIZED;
  633.     newGrpPtr->headerWindow = UNINITIALIZED;
  634.     newGrpPtr->x = UNINITIALIZED;
  635.     newGrpPtr->y = UNINITIALIZED;
  636.     newGrpPtr->width = 0;
  637.     newGrpPtr->height = 0;
  638.     newGrpPtr->fileList = NULL;
  639.     newGrpPtr->groupBindings = NULL;
  640.     newGrpPtr->selectedP = FALSE;
  641.     newGrpPtr->highlightP = FALSE;
  642.     /*
  643.      * If this is too slow, then change this so it does not actually
  644.      * gather the files?
  645.      */
  646.     if (WishGatherSingleGroup(aWindow, newGrpPtr) != TCL_OK) {
  647.     /* eliminate group definition? */
  648.     free(newGrpPtr->rule);
  649.     return TCL_ERROR;
  650.     }
  651.  
  652.     return TCL_OK;
  653. }
  654.  
  655.  
  656. /*
  657.  *----------------------------------------------------------------------
  658.  *
  659.  * WishPrintTclError --
  660.  *
  661.  *    Print out a tcl interpreter error message.
  662.  *
  663.  * Results:
  664.  *    None.
  665.  *
  666.  * Side effects:
  667.  *    None.
  668.  *
  669.  *----------------------------------------------------------------------
  670.  */
  671. void
  672. WishPrintTclError(aWindow)
  673.     WishWindow    *aWindow;
  674. {
  675.     char    c;
  676.     char    stupidBuffer[TCL_RESULT_SIZE + 1];    /* I can't seem to
  677.                                 * print interpreter
  678.                              * result without
  679.                              * copying it! */
  680.  
  681.     /*
  682.      * Capitalize first character of error message.
  683.      */
  684.     c = aWindow->interp->result[0];
  685.     if ((c >= 'a') && (c <= 'z')) {
  686.     aWindow->interp->result[0] += 'A' - 'a';
  687.     }
  688.     strncpy(stupidBuffer, aWindow->interp->result, TCL_RESULT_SIZE);
  689.     /*
  690.      * output message - should call routine that uses 1-line window
  691.      * if possible.
  692.      */
  693.     aWindow->notifierP = TRUE;
  694.     Sx_Notify(wishDisplay, aWindow->surroundingWindow, -1, -1, 0,
  695.         stupidBuffer, NULL, TRUE, "Continue", (char *) NULL);
  696.     aWindow->notifierP = FALSE;
  697.     /* replace lower case */
  698.     aWindow->interp->result[0] = c;
  699.  
  700.     return;
  701. }
  702.  
  703.  
  704. /*
  705.  *----------------------------------------------------------------------
  706.  *
  707.  * WishDefineGroupCmd --
  708.  *
  709.  *    Add the definition of a new group.
  710.  *    Currently, this only will add it to the end of the list of groups.
  711.  *
  712.  * Syntax:
  713.  *    defineGroup defType def
  714.  *
  715.  * Results:
  716.  *    Returns TCL_OK if all went well, or TCL errors if not.
  717.  *
  718.  * Side effects:
  719.  *    A new group will be added.  It will not be displayed until something
  720.  *    causes a redisplay.
  721.  *
  722.  *----------------------------------------------------------------------
  723.  */
  724. int
  725. WishDefineGroupCmd(aWindow, interp, argc, argv)
  726.     WishWindow    *aWindow;
  727.     Tcl_Interp        *interp;
  728.     int            argc;
  729.     char        **argv;
  730. {
  731.     WishGroup    *grpPtr;
  732.     WishGroup    *newGrpPtr;
  733.  
  734.     if (argc != 3) {
  735.     sprintf(interp->result, "%s %s", "Wrong # of args. Must be 3 args,",
  736.         "\"defineGroup defType def\"");
  737.     return TCL_ERROR;
  738.     }
  739.  
  740.     for (grpPtr = aWindow->groupList; grpPtr != NULL;
  741.         grpPtr = grpPtr->nextPtr) {
  742.     if (strcmp(argv[2], grpPtr->rule) == 0) {
  743.         if (grpPtr->fileList == NULL && aWindow->hideEmptyGroupsP) {
  744.             sprintf(interp->result, "%s %s, %s",
  745.             "A group already has the definition", argv[2],
  746.             "but it may not be visible since no files match it.");
  747.         } else {
  748.         sprintf(interp->result, "A group already has the definition %s",
  749.             argv[2]);
  750.         }
  751.         return TCL_ERROR;
  752.     }
  753.     }
  754.  
  755.     newGrpPtr = (WishGroup *) malloc(sizeof (WishGroup));
  756.  
  757.     /* test the new group's definition */
  758.     if (GetNewGroup(aWindow, interp, argv + 1, newGrpPtr) != TCL_OK) {
  759.     free(newGrpPtr);
  760.     WishPrintTclError(aWindow);
  761.     WishDoCmd(aWindow, "close");
  762.     /*
  763.      * It seems weird to return with TCL_ERROR here, but if i don't,
  764.      * something will try to unmap the previously destroyed window.
  765.      * Strangely, nothing wrong seems to happen this way, such as
  766.      * something trying to print out a message in a deleted interpreter...
  767.      */
  768.     return TCL_ERROR;
  769.     }
  770.  
  771.     /* attach new group to end of list */
  772.     if (aWindow->groupList == NULL) {
  773.     aWindow->groupList = newGrpPtr;
  774.     } else {
  775.     for (grpPtr = aWindow->groupList; grpPtr->nextPtr != NULL;
  776.         grpPtr = grpPtr->nextPtr) {
  777.         /* nothing */
  778.     }
  779.     grpPtr->nextPtr = newGrpPtr;
  780.     }
  781.     if (aWindow->dontDisplayChangesP == FALSE) {
  782.     /* update display */
  783.     if (aWindow->firstElement == UNINITIALIZED) {
  784.         aWindow->firstElement = 1;
  785.     }
  786.     WishSetPositions(aWindow);
  787.     }
  788.     return TCL_OK;
  789. }
  790.  
  791.  
  792. /*
  793.  *----------------------------------------------------------------------
  794.  *
  795.  * WishSelectionCmd --
  796.  *
  797.  *    Return the current value of the selection set in interp->result.
  798.  *
  799.  * Syntax:
  800.  *    selection
  801.  *
  802.  * Results:
  803.  *    Returns TCL_OK if all went well, or TCL_ERROR if any sort of
  804.  *    error occurred.
  805.  *
  806.  * Side effects:
  807.  *    None except memory allocation.
  808.  *
  809.  *----------------------------------------------------------------------
  810.  */
  811. /*ARGSUSED*/
  812. int
  813. WishSelectionCmd(aWindow, interp, argc, argv)
  814.     WishWindow    *aWindow;
  815.     Tcl_Interp        *interp;
  816.     int            argc;
  817.     char        **argv;
  818. {
  819.     if (argc > 1) {
  820.     sprintf(interp->result, "Too many args to selection command.");
  821.     return TCL_ERROR;
  822.     }
  823.     return TCL_OK;
  824. }
  825.  
  826.  
  827. /*
  828.  *----------------------------------------------------------------------
  829.  *
  830.  * WishSortFilesCmd --
  831.  *
  832.  *    Add or change the method of sorting directory entries.
  833.  *
  834.  * Syntax:
  835.  *    changeSort [method]
  836.  *
  837.  * Results:
  838.  *    Returns TCL_OK if all went well, or TCL_ERROR if any sort of
  839.  *    error occurred.
  840.  *
  841.  * Side effects:
  842.  *    The order of the files displayed will change if the new sorting
  843.  *    method is different.  In this case, the user will be prompted
  844.  *    to see if he wishes to make the new sorting method the default
  845.  *    method for the directory.
  846.  *
  847.  *----------------------------------------------------------------------
  848.  */
  849. int
  850. WishSortFilesCmd(aWindow, interp, argc, argv)
  851.     WishWindow    *aWindow;
  852.     Tcl_Interp        *interp;
  853.     int            argc;
  854.     char        **argv;
  855. {
  856.     int    which;
  857.  
  858.     if (argc > 3) {
  859.     sprintf(interp->result, "Too many args: 3 arg maximum to sort command");
  860.     return TCL_ERROR;
  861.     }
  862.     if (argc == 1) {
  863.     /*
  864.      * Get the sorting method from the user.  It would be nice if this
  865.      * could all be in one notifier or in a check-list like window, but
  866.      * Sx_Notify puts all the buttons in a single line at the top of the
  867.      * notifier and there are too many buttons to fit on the window here
  868.      * if the "reverse" options are included.  So for now it's done with
  869.      * 2 notifiers, one to get what to sort by and the next to say
  870.      * forwards or backwards.
  871.      *
  872.      * Protect the data structure from redraws initiated in Sx_Notify.
  873.      */
  874.     aWindow->notifierP = TRUE;
  875.     which = Sx_Notify(wishDisplay, aWindow->surroundingWindow, -1, -1, 0,
  876.         "Pick sorting method", NULL, TRUE, "Alpha", "AccessTime",
  877.         "DataModifyTime", "DescriptorModifyTime", "Size",
  878.         (char *) NULL);
  879.     aWindow->notifierP = FALSE;
  880.     switch(which) {
  881.     case 0:
  882.         aWindow->sortingInstructions = WISH_ALPHA_SORT;
  883.         break;
  884.     case 1:
  885.         aWindow->sortingInstructions = WISH_ATIME_SORT;
  886.         break;
  887.     case 2:
  888.         aWindow->sortingInstructions = WISH_MTIME_SORT;
  889.         break;
  890.     case 3:
  891.         aWindow->sortingInstructions = WISH_DTIME_SORT;
  892.         break;
  893.     case 4:
  894.         aWindow->sortingInstructions = WISH_SIZE_SORT;
  895.         break;
  896.     default:
  897.         sprintf(wishErrorMsg, "%s %s", "Something is wrong.",
  898.             "The sorthing method just entered is unrecognized.");
  899.         aWindow->notifierP = TRUE;
  900.         Sx_Notify(wishDisplay, aWindow->surroundingWindow, -1, -1, 0,
  901.             wishErrorMsg, "Skip command", (char *) NULL);
  902.         aWindow->notifierP = FALSE;
  903.         return TCL_ERROR;
  904.     }
  905.     aWindow->notifierP = TRUE;
  906.     which = Sx_Notify(wishDisplay, aWindow->surroundingWindow, -1, -1, 0,
  907.         "Sort in forwards or reverse order?", NULL, TRUE,
  908.         "Forwards", "Reverse", (char *) NULL);
  909.     aWindow->notifierP = FALSE;
  910.     if (which == 1) {
  911.         aWindow->sortingInstructions |= WISH_REVERSE_SORT;
  912.     }
  913.     WishSetSort(aWindow, NULL, NULL);
  914.     } else if (argc == 2) {        /* parse command */
  915.     WishSetSort(aWindow, argv[1], NULL);
  916.     } else {
  917.     WishSetSort(aWindow, argv[1], argv[2]);
  918.     }
  919.     /*
  920.      * Now ask if they want to make this change be the default sort
  921.      * method for this directory.
  922.      */
  923.     /* soon. */
  924.  
  925.     if (aWindow->dontDisplayChangesP == FALSE) {
  926.     /* update display */
  927.     if (aWindow->firstElement == UNINITIALIZED) {
  928.         aWindow->firstElement = 1;
  929.     }
  930.     WishSetPositions(aWindow);
  931.     }
  932.  
  933.     return TCL_OK;
  934. }
  935.  
  936.  
  937. /*
  938.  *----------------------------------------------------------------------
  939.  *
  940.  * WishSetSort --
  941.  *
  942.  *    Change the sorting information and resort files.
  943.  *    If the reverse argument is non-null, do the reverse sort of the
  944.  *    sortMethod argument.  If the sortMethod argument is NULL, then the
  945.  *    aWindow->sortingInstructions field is already set.
  946.  *
  947.  * Results:
  948.  *    None.
  949.  *
  950.  * Side effects:
  951.  *    The order of the files displayed will change if the new sorting
  952.  *    method is different.
  953.  *
  954.  *----------------------------------------------------------------------
  955.  */
  956. void
  957. WishSetSort(aWindow, sortMethod, reverse)
  958.     WishWindow    *aWindow;
  959.     char        *sortMethod;
  960.     char        *reverse;
  961. {
  962.     int        getAttrsP = FALSE;
  963.     WishFile    *tmpPtr;
  964.     WishGroup    *grpPtr;
  965.     int        (*compareProc)();
  966.     WishFile    **fileArray;
  967.     int        i, arraySize;
  968.  
  969.     if (sortMethod != NULL) {
  970.     aWindow->sortingInstructions = 0;
  971.     if (reverse != NULL) {
  972.         aWindow->sortingInstructions |= WISH_REVERSE_SORT;
  973.     }
  974.     if (strcmp(sortMethod, "alpha") == 0 ||
  975.         strcmp(sortMethod, "Alpha") == 0) {
  976.         aWindow->sortingInstructions |= WISH_ALPHA_SORT;
  977.     }
  978.     if (strcmp(sortMethod, "accessTime") == 0 ||
  979.         strcmp(sortMethod, "AccessTime") == 0 ||
  980.         strcmp(sortMethod, "atime") == 0 ||
  981.         strcmp(sortMethod, "aTime") == 0 ||
  982.         strcmp(sortMethod, "Atime") == 0) {
  983.         aWindow->sortingInstructions |= WISH_ATIME_SORT;
  984.     }
  985.     if (strcmp(sortMethod, "modifyTime") == 0 ||
  986.         strcmp(sortMethod, "ModifyTime") == 0 ||
  987.         strcmp(sortMethod, "datamodtime") == 0 ||
  988.         strcmp(sortMethod, "dataModTime") == 0 ||
  989.         strcmp(sortMethod, "DataModTime") == 0 ||
  990.         strcmp(sortMethod, "datamodifytime") == 0 ||
  991.         strcmp(sortMethod, "dataModifyTime") == 0 ||
  992.         strcmp(sortMethod, "DataModifyTime") == 0 ||
  993.         strcmp(sortMethod, "mtime") == 0 ||
  994.         strcmp(sortMethod, "mTime") == 0 ||
  995.         strcmp(sortMethod, "Mtime") == 0) {
  996.         aWindow->sortingInstructions |= WISH_MTIME_SORT;
  997.     }
  998.     if (strcmp(sortMethod, "descModifyTime") == 0 ||
  999.         strcmp(sortMethod, "DescModifyTime") == 0 ||
  1000.         strcmp(sortMethod, "descmodifytime") == 0 ||
  1001.         strcmp(sortMethod, "descmodtime") == 0 ||
  1002.         strcmp(sortMethod, "descModTime") == 0 ||
  1003.         strcmp(sortMethod, "DescModTime") == 0 ||
  1004.         strcmp(sortMethod, "descriptormodifytime") == 0 ||
  1005.         strcmp(sortMethod, "descriptorModifyTime") == 0 ||
  1006.         strcmp(sortMethod, "DescriptorModifyTime") == 0 ||
  1007.         strcmp(sortMethod, "dtime") == 0 ||
  1008.         strcmp(sortMethod, "dTime") == 0 ||
  1009.         strcmp(sortMethod, "Dtime") == 0) {
  1010.         aWindow->sortingInstructions |= WISH_DTIME_SORT;
  1011.     }
  1012.     if (strcmp(sortMethod, "size") == 0 ||
  1013.         strcmp(sortMethod, "Size") == 0) {
  1014.         aWindow->sortingInstructions |= WISH_SIZE_SORT;
  1015.     }
  1016.     }
  1017.  
  1018.     /* Now sort the files -- get attr's if necessary */
  1019.     getAttrsP = WISH_ATTR_NECESSARY_P;
  1020.     if (aWindow->groupList == NULL) {
  1021.     return;
  1022.     }
  1023.     /* need comparison function that takes ptrs to WishFiles */
  1024.     WishGetCompareProc(aWindow, &compareProc, TRUE);
  1025.     for (grpPtr = aWindow->groupList; grpPtr != NULL;
  1026.         grpPtr = grpPtr->nextPtr) {
  1027.     /* if 0 or 1 files, don't sort */
  1028.     if (grpPtr->fileList == NULL || grpPtr->fileList->nextPtr == NULL) {
  1029.         continue;
  1030.     }
  1031.     for (i = 0, tmpPtr = grpPtr->fileList; tmpPtr != NULL;
  1032.         tmpPtr = tmpPtr->nextPtr, i++) {
  1033.         if (getAttrsP && tmpPtr->attrPtr == NULL) {
  1034.         tmpPtr->attrPtr = (struct stat *)
  1035.             malloc(sizeof (struct stat));
  1036.         if (lstat(tmpPtr->name, tmpPtr->attrPtr) != 0) {
  1037.             /* skip this file */
  1038.             sprintf(wishErrorMsg, "%s %s.",
  1039.                 "Couldn't get attributes for file", tmpPtr->name);
  1040.             aWindow->notifierP = TRUE;
  1041.             Sx_Notify(wishDisplay, aWindow->surroundingWindow, -1, -1,
  1042.                 0, wishErrorMsg, NULL, TRUE, "Continue",
  1043.                 (char *) NULL);
  1044.             aWindow->notifierP = FALSE;
  1045.             bzero((char *) tmpPtr->attrPtr, sizeof (struct stat));
  1046.         }
  1047.         }
  1048.     }
  1049.     arraySize = i;
  1050.     fileArray = (WishFile **) malloc(arraySize *
  1051.         sizeof (WishFile *));
  1052.     for (tmpPtr = grpPtr->fileList, i = 0; tmpPtr != NULL;
  1053.         tmpPtr = tmpPtr->nextPtr, i++) {
  1054.         fileArray[i] = tmpPtr;
  1055.     }
  1056.     qsort(fileArray, arraySize, sizeof (WishFile *), compareProc);
  1057.     for (i = 0; i < arraySize - 1; i++) {
  1058.         fileArray[i]->nextPtr = fileArray[i+1];
  1059.     }
  1060.     fileArray[arraySize - 1]->nextPtr = NULL;
  1061.     grpPtr->fileList = fileArray[0];
  1062.     free(fileArray);
  1063.     }
  1064.  
  1065.     return;
  1066. }
  1067.  
  1068.  
  1069.  
  1070. /*
  1071.  *----------------------------------------------------------------------
  1072.  *
  1073.  * WishCloseCmd --
  1074.  *
  1075.  *    Close the window.  This should do some more cleaning up.
  1076.  *
  1077.  * Syntax:
  1078.  *    close
  1079.  *
  1080.  * Results:
  1081.  *    TCL_OK.
  1082.  *
  1083.  * Side effects:
  1084.  *    Closes the window, deletes the Tcl interpreter for the window, and
  1085.  *    frees up the window data structures.  If this was the last window,
  1086.  *    we may exit.
  1087.  *
  1088.  *----------------------------------------------------------------------
  1089.  */
  1090. /*ARGSUSED*/
  1091. int
  1092. WishCloseCmd(aWindow, interp, argc, argv)
  1093.     register    WishWindow    *aWindow;
  1094.     Tcl_Interp    *interp;
  1095.     int        argc;
  1096.     char    **argv;
  1097. {
  1098.     if (!MonClient_DeleteDir(aWindow->dir,
  1099.         (ClientData) aWindow->surroundingWindow)) {
  1100.     /* what should I do here?  Does it matter? */
  1101.     }
  1102.     XDeleteContext(wishDisplay, aWindow->surroundingWindow,
  1103.         wishWindowContext);
  1104.     /* DeleteHandlers? */
  1105.     WishGarbageCollect(aWindow);
  1106.     XDestroyWindow(wishDisplay, aWindow->surroundingWindow);
  1107.     Tcl_DeleteInterp(aWindow->interp);
  1108.     free(aWindow);
  1109.     wishWindowCount--;
  1110.     if (wishWindowCount <= 0) {
  1111.     exit(0);
  1112.     }
  1113.     return TCL_OK;
  1114. }
  1115.  
  1116.  
  1117. /*
  1118.  *----------------------------------------------------------------------
  1119.  *
  1120.  * WishExecCmd --
  1121.  *
  1122.  *    Execute a command in an associated shell window.
  1123.  *
  1124.  * Syntax:
  1125.  *    exec command
  1126.  *
  1127.  * Results:
  1128.  *    Returns TCL_OK if all went well, or TCL_ERROR if an associated
  1129.  *    shell window could not be found or there was no shell command
  1130.  *    given.  Whether or not the command
  1131.  *    given to the shell executes happily is another matter.  Maybe I'll
  1132.  *    decide that its return status should be available, but I don't know
  1133.  *    how to do that...
  1134.  *
  1135.  * Side effects:
  1136.  *    Almost anything -- it depends what the shell command does.
  1137.  *
  1138.  *----------------------------------------------------------------------
  1139.  */
  1140. /*ARGSUSED*/
  1141. int
  1142. WishExecCmd(aWindow, interp, argc, argv)
  1143.     WishWindow    *aWindow;
  1144.     Tcl_Interp    *interp;
  1145.     int        argc;
  1146.     char    **argv;
  1147. {
  1148.     int        i;
  1149.     char    *command;
  1150.     int        length;
  1151.     int        result;
  1152.  
  1153.     length = argc - 1;    /* spaces between command words + insert word */
  1154.     for (i = 1; i < argc; i++) {    /* skip first "exec" word */
  1155.     length += strlen(argv[i]);
  1156.     }
  1157.     length += strlen("insert");
  1158.     command = (char *) malloc(length + 1);    /* plus null char */
  1159.     strcpy(command, "insert");
  1160.     for (i = 1; i < argc; i++) {    /* skip first "exec" word */
  1161.     strcat(command, " ");
  1162.     strcat(command, argv[i]);
  1163.     }
  1164.     result = Tx_Command(wishDisplay, aWindow->txOutsideWindow, command);
  1165.     free(command);
  1166.  
  1167.     return result;
  1168. }
  1169.  
  1170.  
  1171. /*
  1172.  *----------------------------------------------------------------------
  1173.  *
  1174.  * WishGroupBindCmd --
  1175.  *
  1176.  *    Add a command binding to a group.  This will bind a command
  1177.  *    to a particular button (or button combination).
  1178.  *
  1179.  * Syntax:
  1180.  *    groupbind groupname buttonMask command
  1181.  *
  1182.  * Results:
  1183.  *    Returns TCL_OK if all went well, or TCL errors if not.
  1184.  *
  1185.  * Side effects:
  1186.  *    A new command binding will be added to the group.
  1187.  *
  1188.  *----------------------------------------------------------------------
  1189.  */
  1190. int
  1191. WishGroupBindCmd(aWindow, interp, argc, argv)
  1192.     WishWindow    *aWindow;
  1193.     Tcl_Interp        *interp;
  1194.     int            argc;
  1195.     char        **argv;
  1196. {
  1197.     WishGroup    *grpPtr;
  1198.     char    **buttonArgv;
  1199.     int        buttonArgc;
  1200.     int        button = 0;
  1201.     int        test;
  1202.     int        i;
  1203.  
  1204.     if (argc != 4) {
  1205.     sprintf(interp->result,
  1206.         "Wrong # of args to groupBind command, %s",
  1207.         "\"groupBind groupName buttonMask command\"");
  1208.     return TCL_ERROR;
  1209.     }
  1210.     for (grpPtr = aWindow->groupList; grpPtr != NULL;
  1211.         grpPtr = grpPtr->nextPtr) {
  1212.     if (strcmp(grpPtr->rule, argv[1]) == 0) {
  1213.         break;
  1214.     }
  1215.     }
  1216.     if (grpPtr == NULL) {
  1217.     sprintf(interp->result, "No such group %s", argv[1]);
  1218.     return TCL_ERROR;
  1219.     }
  1220.  
  1221.     if (Tcl_SplitList(interp, argv[2], &buttonArgc, &buttonArgv) != TCL_OK) {
  1222.     return TCL_ERROR;
  1223.     }
  1224.     for (i = 0; i < buttonArgc; i++) {
  1225.     test = WishWhichButton(buttonArgv[i]);
  1226.     if (test == 0) {
  1227.         sprintf(interp->result, "Bad button name %s", argv[i]);
  1228.         /* will this really free it?  Tcl man page says so... */
  1229.         free(buttonArgv);
  1230.         return TCL_ERROR;
  1231.     }
  1232.     button |= test;
  1233.     }
  1234.     /* will this really free it?  Tcl man page says so... */
  1235.     free(buttonArgv);
  1236.     WishAddGroupBinding(grpPtr, button, argv[3]);
  1237.  
  1238.     return TCL_OK;
  1239. }
  1240.  
  1241.  
  1242. /*
  1243.  *----------------------------------------------------------------------
  1244.  *
  1245.  * WishMenuCmd --
  1246.  *
  1247.  *    Create, delete, and modify menus.
  1248.  *
  1249.  * Syntax:
  1250.  *    menu append name leftText centerText rightText color cmd
  1251.  *    menu create name leftText centerText rightText color cmd leftText ...
  1252.  *    menu delete name
  1253.  *    menu modify name entryIndex leftText centerText rightText color cmd
  1254.  *
  1255.  * Results:
  1256.  *    Returns TCL_OK if all went well, or TCL_ERROR if any sort of
  1257.  *    error occurred.
  1258.  *
  1259.  * Side effects:
  1260.  *    The menu structure for the current window gets modified.
  1261.  *
  1262.  *----------------------------------------------------------------------
  1263.  */
  1264. int
  1265. WishMenuCmd(aWindow, interp, argc, argv)
  1266.     register    WishWindow    *aWindow;
  1267.     Tcl_Interp    *interp;
  1268.     int        argc;
  1269.     char    **argv;
  1270. {
  1271. #ifdef NOTDEF
  1272.     MxMenuInfo *miPtr;
  1273. #endif NOTDEF
  1274.     int length;
  1275.     Sx_MenuEntry entries[SX_MAX_MENU_ENTRIES];
  1276.  
  1277.     static char *GetString();        /* Forward references */
  1278.  
  1279.     if (argc < 2) {
  1280.     sprintf(interp->result, "wrong # args: must be \"%.50s option [args]\"",
  1281.         argv[0]);
  1282.     return TCL_ERROR;
  1283.     }
  1284.     length = strlen(argv[1]);
  1285.     if (strncmp(argv[1], "append", length) == 0) {
  1286.     Window window;
  1287.     XFontStruct *fontPtr;
  1288.     int numEntries;
  1289.     unsigned long fg, bg;
  1290.  
  1291.     if (argc != 8) {
  1292.         sprintf(interp->result, "wrong # args: should be \"%.50s append name left center right color cmd\"",
  1293.             argv[0]);
  1294.         return TCL_ERROR;
  1295.     }
  1296.     window = Sx_MenuGetWindow(wishDisplay, aWindow->menuBar, argv[2]);
  1297.     if (window == NULL) {
  1298.         goto menuNameBad;
  1299.     }
  1300.     numEntries = Sx_MenuGetInfo(wishDisplay, window, entries, &fontPtr,
  1301.         &fg, &bg);
  1302.     if (numEntries >= SX_MAX_MENU_ENTRIES) {
  1303.         return TCL_OK;
  1304.     }
  1305.     entries[numEntries].leftText = GetString(argv[3]);
  1306.     entries[numEntries].centerText = GetString(argv[4]);
  1307.     entries[numEntries].rightText = GetString(argv[5]);
  1308.     entries[numEntries].background =
  1309.         Util_StringToColor(wishDisplay, argv[6]);
  1310. /* should check to see if it returns -1 value!!!! */
  1311.     entries[numEntries].proc = WishMenuProc;
  1312.     /* Mx uses a structure with both command AND aWindow fields.  Should I? */
  1313.     entries[numEntries].clientData = (ClientData) Util_Strcpy(NULL,
  1314.         argv[7]);
  1315.     Sx_MenuCreate(wishDisplay, aWindow->menuBar, argv[2], numEntries+1,
  1316.         entries, fontPtr, fg, bg);
  1317.     return TCL_OK;
  1318.     }
  1319.     if (strncmp(argv[1], "create", length) == 0) {
  1320.     int numEntries, i, arg;
  1321.  
  1322.     numEntries = (argc - 3)/5;
  1323.     if ((argc-3) != (numEntries*5)) {
  1324.         sprintf(interp->result, "wrong # args: should be \"%.50s create name [left center right color cmd] ...\"",
  1325.             argv[0]);
  1326.         return TCL_ERROR;
  1327.     }
  1328.     if (numEntries > SX_MAX_MENU_ENTRIES) {
  1329.         sprintf(interp->result,
  1330.             "can't create a menu with more than %d entries",
  1331.             SX_MAX_MENU_ENTRIES);
  1332.         return TCL_ERROR;
  1333.     }
  1334.  
  1335.     for (i = 0, arg = 3; i < numEntries; i++, arg += 5) {
  1336.         entries[i].leftText = GetString(argv[arg]);
  1337.         entries[i].centerText = GetString(argv[arg+1]);
  1338.         entries[i].rightText = GetString(argv[arg+2]);
  1339.         if (strcmp(argv[arg+3], "-") != 0) {
  1340.         entries[i].background =
  1341.             Util_StringToColor(wishDisplay, argv[arg+3]);
  1342.         } else {
  1343.         entries[i].background = aWindow->menuBackground;
  1344.         }
  1345. /* should check to see if it returns -1 value! */
  1346.         entries[i].proc = WishMenuProc;
  1347.         entries[i].clientData = (ClientData) Util_Strcpy(NULL, argv[arg+4]);
  1348.     }
  1349.     Sx_MenuCreate(wishDisplay, aWindow->menuBar, argv[2], numEntries,
  1350.         entries, aWindow->fontPtr, aWindow->menuForeground,
  1351.         aWindow->menuBackground);
  1352.     return TCL_OK;
  1353.     } else if (strncmp(argv[1], "delete",length) == 0) {
  1354.     Window window;
  1355.     int count;
  1356.  
  1357.     if (argc != 3) {
  1358.         sprintf(interp->result,
  1359.             "wrong # args: should be \"%.50s delete name\"",
  1360.             argv[0]);
  1361.         return TCL_ERROR;
  1362.     }
  1363.     window = Sx_MenuGetWindow(wishDisplay, aWindow->menuBar, argv[2]);
  1364.     if (window == NULL) {
  1365.         goto menuNameBad;
  1366.     }
  1367.     count = Sx_MenuGetInfo(wishDisplay, window, entries,
  1368.         (XFontStruct **) NULL, (int *) NULL, (int *) NULL);
  1369.     for (count--; count >= 0; count--) {
  1370.         free(entries[count].clientData);
  1371.     }
  1372.     XDestroyWindow(wishDisplay, window);
  1373.     return TCL_OK;
  1374. #ifdef NOTDEF
  1375.     } else if (strncmp(argv[1], "info", length) == 0) {
  1376.     Window window;
  1377.     int count, i;
  1378.     char *names[SX_MAX_MENUS];
  1379.     char *entryStrings[SX_MAX_MENU_ENTRIES];
  1380.     char *pieces[4];
  1381.  
  1382.     if (argc == 2) {
  1383.         count = Sx_MenuGetNames(mxwPtr->display, mxwPtr->menuBar, names,
  1384.             (Window *) NULL);
  1385.         interp->result = Tcl_Merge(count, names);
  1386.         interp->dynamic = 1;
  1387.         return TCL_OK;
  1388.     }
  1389.     if (argc != 3) {
  1390.         sprintf(interp->result,
  1391.             "wrong # args: should be \"%.50s info [name]\"",
  1392.             argv[0]);
  1393.         return TCL_ERROR;
  1394.     }
  1395.     window = Sx_MenuGetWindow(mxwPtr->display, mxwPtr->menuBar, argv[2]);
  1396.     if (window == NULL) {
  1397.         goto menuNameBad;
  1398.     }
  1399.     count = Sx_MenuGetInfo(mxwPtr->display, window, entries,
  1400.         (XFontStruct **) NULL, (unsigned long *) NULL,
  1401.         (unsigned long *) NULL);
  1402.     for (i = 0; i < count; i++) {
  1403.         pieces[0] = entries[i].leftText;
  1404.         if (pieces[0] == NULL) {
  1405.         pieces[0] = "";
  1406.         }
  1407.         pieces[1] = entries[i].centerText;
  1408.         if (pieces[1] == NULL) {
  1409.         pieces[1] = "";
  1410.         }
  1411.         pieces[2] = entries[i].rightText;
  1412.         if (pieces[2] == NULL) {
  1413.         pieces[2] = "";
  1414.         }
  1415.         pieces[3] = ((MxMenuInfo *) entries[i].clientData)->command;
  1416.         if (pieces[3] == NULL) {
  1417.         pieces[3] = "";
  1418.         }
  1419.         entryStrings[i] = Tcl_Merge(4, pieces);
  1420.     }
  1421.     interp->result = Tcl_Merge(count, entryStrings);
  1422.     interp->dynamic = 1;
  1423.     for (i = 0; i < count; i++) {
  1424.         free(entryStrings[i]);
  1425.     }
  1426.     return TCL_OK;
  1427. #endif NOTDEF
  1428.     } else if (strncmp(argv[1], "modify", length) == 0) {
  1429.     Window window;
  1430.     Sx_MenuEntry entry;
  1431.     int index;
  1432.  
  1433.     if (argc != 9) {
  1434.         sprintf(interp->result, "wrong # args: should be \"%.50s modify name index left center right color cmd\"",
  1435.             argv[0]);
  1436.         return TCL_ERROR;
  1437.     }
  1438.     window = Sx_MenuGetWindow(wishDisplay, aWindow->menuBar, argv[2]);
  1439.     if (window == NULL) {
  1440.         goto menuNameBad;
  1441.     }
  1442.     index = atoi(argv[3]);
  1443.     if ((index < 0) || (index >= Sx_MenuGetInfo(wishDisplay, window,
  1444.         entries, (XFontStruct **) NULL, (int *) NULL, (int *) NULL))) {
  1445.         sprintf(interp->result,
  1446.             "there's no entry %d in menu \"%.50s\"",
  1447.             index, argv[2]);
  1448.         return TCL_ERROR;
  1449.     }
  1450.     free(entries[index].clientData);
  1451.     entry.leftText = GetString(argv[4]);
  1452.     entry.centerText = GetString(argv[5]);
  1453.     entry.rightText = GetString(argv[6]);
  1454. /* should check to see if it returns -1 value! */
  1455.     entry.background = Util_StringToColor(wishDisplay, argv[7]);
  1456.     entry.proc = WishMenuProc;
  1457.     entry.clientData = (ClientData) Util_Strcpy(NULL, argv[8]);
  1458.     Sx_MenuReplaceEntry(wishDisplay, window, index, &entry);
  1459.     return TCL_OK;
  1460.     } else {
  1461.     sprintf(interp->result, "bad \"%.50s\" option: must be append, create, delete, or modify\"",
  1462.         argv[0]);
  1463.     return TCL_ERROR;
  1464.     }
  1465.  
  1466.     menuNameBad:
  1467.     sprintf(interp->result, "there's no menu named \"%.50s\".", argv[2]);
  1468.     return TCL_ERROR;
  1469. }
  1470.  
  1471.  
  1472.  
  1473. /*
  1474.  *----------------------------------------------------------------------
  1475.  *
  1476.  * WishOpenCmd --
  1477.  *
  1478.  *    Create another wish window.
  1479.  *    Eventually this will be create another Wish window, with an
  1480.  *    argument to say whether it should be a flat or tree window.  If
  1481.  *    the argument is not supplied, it should default to the type of
  1482.  *    window where the command was called from.
  1483.  *
  1484.  * Syntax:
  1485.  *    open directory
  1486.  *
  1487.  * Results:
  1488.  *    Returns TCL_OK if all went well, various TCL errors if not.
  1489.  *
  1490.  * Side effects:
  1491.  *    A new window should be created.
  1492.  *
  1493.  *----------------------------------------------------------------------
  1494.  */
  1495. int
  1496. WishOpenCmd(aWindow, interp, argc, argv)
  1497.     WishWindow    *aWindow;
  1498.     Tcl_Interp        *interp;
  1499.     int            argc;
  1500.     char        **argv;
  1501. {
  1502.     if (argc > 2) {
  1503.     sprintf(interp->result, "%s %s", "Too many args to open command:",
  1504.         "open [directory]");
  1505.     return TCL_ERROR;
  1506.     }
  1507.     if (argc == 2) {
  1508.     if (WishCreate(aWindow, argv[1]) == NULL) {
  1509.         return TCL_ERROR;
  1510.     }
  1511.     } else if (WishCreate(aWindow, NULL) == NULL) {
  1512.     return TCL_ERROR;
  1513.     }
  1514.  
  1515.     return TCL_OK;
  1516. }
  1517.  
  1518.  
  1519. /*
  1520.  *----------------------------------------------------------------------
  1521.  *
  1522.  * WishPatternCompareCmd --
  1523.  *
  1524.  *    Compare two strings.  In the interpreter, return 0 for a match,
  1525.  *    and < 0 for an error.  Return > 0 for failure to match.
  1526.  *
  1527.  * Syntax:
  1528.  *    pattern    string1 string2
  1529.  *
  1530.  * Results:
  1531.  *    TCL_OK if everything went okay, TCL_ERROR if not.
  1532.  *
  1533.  * Side effects:
  1534.  *    None.
  1535.  *
  1536.  *----------------------------------------------------------------------
  1537.  */
  1538. /*ARGSUSED*/
  1539. int
  1540. WishPatternCompareCmd(aWindow, interp, argc, argv)
  1541.     register    WishWindow    *aWindow;
  1542.     Tcl_Interp    *interp;
  1543.     int        argc;
  1544.     char    **argv;
  1545. {
  1546.     int    result;
  1547.  
  1548.     if (argc != 3) {
  1549.     sprintf(interp->result, "pattern command requires 3 args");
  1550.     return TCL_ERROR;
  1551.     }
  1552.     result = Pattern_Match(argv[1], argv[2]);
  1553.     sprintf(interp->result, "%d", result);
  1554.  
  1555.     return TCL_OK;
  1556. }
  1557.  
  1558.  
  1559. /*
  1560.  *----------------------------------------------------------------------
  1561.  *
  1562.  * WishQuitCmd --
  1563.  *
  1564.  *    Exit the program.
  1565.  *
  1566.  * Syntax:
  1567.  *    quit
  1568.  *
  1569.  * Results:
  1570.  *    Exits.
  1571.  *
  1572.  * Side effects:
  1573.  *    Exits.
  1574.  *
  1575.  *----------------------------------------------------------------------
  1576.  */
  1577. /*ARGSUSED*/
  1578. int
  1579. WishQuitCmd(aWindow, interp, argc, argv)
  1580.     register    WishWindow    *aWindow;
  1581.     Tcl_Interp    *interp;
  1582.     int        argc;
  1583.     char    **argv;
  1584. {
  1585.     /*
  1586.      * I should go through and call MonClient_DeleteDir for each remaining 
  1587.      * window.
  1588.      */
  1589.     exit(0);
  1590. }
  1591.  
  1592. #ifdef NOTDEF
  1593.  
  1594. /*
  1595.  *----------------------------------------------------------------------
  1596.  *
  1597.  * WishRestartCmd --
  1598.  *
  1599.  *    Resource the startup files and rebuild the display of the current
  1600.  *    directory.
  1601.  *
  1602.  * Syntax:
  1603.  *    restart
  1604.  *
  1605.  * Results:
  1606.  *    Returns TCL_OK if all went well, or TCL_ERROR if any sort of
  1607.  *    error occurred.
  1608.  *
  1609.  * Side effects:
  1610.  *    The display may change if the startup files have changed.
  1611.  *
  1612.  *----------------------------------------------------------------------
  1613.  */
  1614. int
  1615. WishRestartCmd(aWindow, interp, argc, argv)
  1616.     WishWindow    *aWindow;
  1617.     int            argc;
  1618.     char        **argv;
  1619. {
  1620.     if (argc != 1) {
  1621.     sprintf(interp->result, "too many args to restart command. 1 arg max");
  1622.     return TCL_ERROR;
  1623.     }
  1624.     WishGarbageCollect(aWindow);
  1625.     /* delete interpreter and start new one? */
  1626.     if (WishGatherNames(aWindow) != TCL_OK) {
  1627.     /* Fix here too. */
  1628.     }
  1629.     /* should it repick the size here if aWindow->pickSizeP is true? */
  1630.     aWindow->firstElement = 1;
  1631.     WishSetPositions(aWindow);
  1632.     /* WishRedraw will be called from event caused in WishSetPositions() */
  1633.  
  1634.     return TCL_OK;
  1635. }
  1636. #endif NOTDEF
  1637.  
  1638.  
  1639. /*
  1640.  *----------------------------------------------------------------------
  1641.  *
  1642.  * WishRedrawCmd --
  1643.  *
  1644.  *    Redraw the window.
  1645.  *
  1646.  * Syntax:
  1647.  *    redraw
  1648.  *
  1649.  * Results:
  1650.  *    TCL_OK.
  1651.  *
  1652.  * Side effects:
  1653.  *    Redraws the window.
  1654.  *
  1655.  *----------------------------------------------------------------------
  1656.  */
  1657. /*ARGSUSED*/
  1658. int
  1659. WishRedrawCmd(aWindow, interp, argc, argv)
  1660.     register    WishWindow    *aWindow;
  1661.     Tcl_Interp    *interp;
  1662.     int        argc;
  1663.     char    **argv;
  1664. {
  1665.     if (aWindow->dontDisplayChangesP == FALSE) {
  1666.     WishRedraw(aWindow);
  1667.     }
  1668.     return TCL_OK;
  1669. }
  1670.  
  1671.  
  1672. /*
  1673.  *----------------------------------------------------------------------
  1674.  *
  1675.  * WishResizeCmd --
  1676.  *
  1677.  *    Resize the window.
  1678.  *
  1679.  * Syntax:
  1680.  *    redraw newheight newwidth
  1681.  *
  1682.  * Results:
  1683.  *    TCL_OK if everything goes well, TCL_ERROR if not.
  1684.  *
  1685.  * Side effects:
  1686.  *    Resizes the window.
  1687.  *
  1688.  *----------------------------------------------------------------------
  1689.  */
  1690. int
  1691. WishResizeCmd(aWindow, interp, argc, argv)
  1692.     register    WishWindow    *aWindow;
  1693.     Tcl_Interp    *interp;
  1694.     int        argc;
  1695.     char    **argv;
  1696. {
  1697.     int    height, width;
  1698.     char    *cptr;
  1699.  
  1700.     if (argc != 3) {
  1701.     sprintf(interp->result, "Wrong # of args to resize command: %s",
  1702.         "\"resize newheight newwidth\"");
  1703.     return TCL_ERROR;
  1704.     }
  1705.     if ((height = strtol(argv[1], &cptr, 10)) == 0 && cptr == argv[1]) {
  1706.     sprintf(interp->result, "Bad height arg to resize command: %s",
  1707.         argv[1]);
  1708.     return TCL_ERROR;
  1709.     }
  1710.     if ((width = strtol(argv[2], &cptr, 10)) == 0 && cptr == argv[2]) {
  1711.     sprintf(interp->result, "Bad width arg to resize command: %s", argv[2]);
  1712.     return TCL_ERROR;
  1713.     }
  1714.     
  1715.     WishSetWindowAndRowInfo(aWindow, height, width);
  1716.  
  1717.     if (aWindow->dontDisplayChangesP == FALSE) {
  1718.     /* update display */
  1719.     if (aWindow->firstElement == UNINITIALIZED) {
  1720.         aWindow->firstElement = 1;
  1721.     }
  1722.     WishSetPositions(aWindow);
  1723.     /*
  1724.      * WishRedraw will be called from event caused in WishSetPostions().
  1725.      */
  1726.     }
  1727.  
  1728.     return TCL_OK;
  1729. }
  1730.  
  1731.  
  1732. /*
  1733.  *----------------------------------------------------------------------
  1734.  *
  1735.  * WishToggleSelEntryCmd --
  1736.  *
  1737.  *    Toggle the selection status of the given entry.
  1738.  *
  1739.  * Syntax:
  1740.  *    toggleSelectionEntry x y [wholeLine]
  1741.  *
  1742.  * Results:
  1743.  *    TCL_OK if everything went well.  TCL_ERROR if not.
  1744.  *
  1745.  * Side effects:
  1746.  *    Toggles the chosen entry.
  1747.  *
  1748.  *----------------------------------------------------------------------
  1749.  */
  1750. /*ARGSUSED*/
  1751. int
  1752. WishToggleSelEntryCmd(aWindow, interp, argc, argv)
  1753.     register    WishWindow    *aWindow;
  1754.     Tcl_Interp    *interp;
  1755.     int        argc;
  1756.     char    **argv;
  1757. {
  1758.     int        x, y;
  1759.     WishFile    *filePtr;
  1760.     WishGroup    *groupPtr = NULL;
  1761.     int    lineP = 0;
  1762.     char    *cptr;
  1763.  
  1764.     if (argc < 3 || argc > 4) {
  1765.     sprintf(aWindow->interp->result, "Wrong # of args to toggleSelection");
  1766.     return TCL_ERROR;
  1767.     }
  1768.     if ((x = strtol(argv[1], &cptr, 10)) == 0 && cptr == argv[1]) {
  1769.     sprintf(aWindow->interp->result, "Bad window x coordinate %s", argv[1]);
  1770.     return TCL_ERROR;
  1771.     }
  1772.     if ((y = strtol(argv[2], &cptr, 10)) == 0 && cptr == argv[2]) {
  1773.     sprintf(aWindow->interp->result, "Bad window y coordinate %s", argv[2]);
  1774.     return TCL_ERROR;
  1775.     }
  1776.     if (argc == 4) {
  1777.     if ((lineP = strtol(argv[3], &cptr, 10)) == 0 && cptr == argv[3]) {
  1778.         sprintf(aWindow->interp->result, "Bad 3rd arg to toggleSelection");
  1779.         return TCL_ERROR;
  1780.     }
  1781.     }
  1782.     
  1783.     filePtr = WishMapCoordsToFile(aWindow, x, y);
  1784.     if (filePtr == NULL && groupPtr == NULL) {
  1785.     sprintf(aWindow->interp->result,
  1786.         "No entry to select at those coordinates: %d %d", x, y);
  1787.     return TCL_ERROR;
  1788.     }
  1789.     if (filePtr != NULL) {
  1790.     WishChangeSelection(aWindow, (ClientData) filePtr, TRUE, lineP, TRUE);
  1791.     } else {
  1792.     WishChangeSelection(aWindow, (ClientData) groupPtr, FALSE, FALSE,
  1793.         TRUE);
  1794.     }
  1795.  
  1796.     return TCL_OK;
  1797. }
  1798.  
  1799. /*
  1800.  *----------------------------------------------------------------------
  1801.  *
  1802.  * WishToggleSelectionCmd --
  1803.  *
  1804.  *    Select or deselect given file.
  1805.  *
  1806.  * Syntax:
  1807.  *    toggleSelection x y [wholeLine]
  1808.  *
  1809.  * Results:
  1810.  *    TCL_OK if everything went well.  TCL_ERROR if not.
  1811.  *
  1812.  * Side effects:
  1813.  *    Changes the selection variable to be given file or empty.
  1814.  *
  1815.  *----------------------------------------------------------------------
  1816.  */
  1817. /*ARGSUSED*/
  1818. int
  1819. WishToggleSelectionCmd(aWindow, interp, argc, argv)
  1820.     register    WishWindow    *aWindow;
  1821.     Tcl_Interp    *interp;
  1822.     int        argc; char    **argv;
  1823. {
  1824.     int        x, y;
  1825.     WishFile    *filePtr = NULL;
  1826.     WishGroup    *groupPtr = NULL;        /* not used yet */
  1827.     int    lineP = 0;
  1828.     char    *cptr;
  1829.  
  1830.     if (argc < 3 || argc > 4) {
  1831.     sprintf(aWindow->interp->result, "Wrong # of args to toggleSelection");
  1832.     return TCL_ERROR;
  1833.     }
  1834.     if ((x = strtol(argv[1], &cptr, 10)) == 0 && cptr == argv[1]) {
  1835.     sprintf(aWindow->interp->result, "Bad window x coordinate %s", argv[1]);
  1836.     return TCL_ERROR;
  1837.     }
  1838.     if ((y = strtol(argv[2], &cptr, 10)) == 0 && cptr == argv[2]) {
  1839.     sprintf(aWindow->interp->result, "Bad window y coordinate %s", argv[2]);
  1840.     return TCL_ERROR;
  1841.     }
  1842.     if (argc == 4) {
  1843.     if ((lineP = strtol(argv[3], &cptr, 10)) == 0 && cptr == argv[3]) {
  1844.         sprintf(aWindow->interp->result, "Bad 3rd arg to toggleSelection");
  1845.         return TCL_ERROR;
  1846.     }
  1847.     }
  1848.     
  1849.     filePtr = WishMapCoordsToFile(aWindow, x, y);
  1850.     if (filePtr == NULL && groupPtr == NULL) {
  1851.     sprintf(aWindow->interp->result,
  1852.         "No entry to select at those coordinates: %d %d", x, y);
  1853.     return TCL_ERROR;
  1854.     }
  1855.     if (filePtr != NULL) {
  1856.     WishChangeSelection(aWindow, (ClientData) filePtr, TRUE, lineP,
  1857.         FALSE);
  1858.     } else {
  1859.     WishChangeSelection(aWindow, (ClientData) groupPtr, FALSE, FALSE,
  1860.         FALSE);
  1861.     }
  1862.  
  1863.     return TCL_OK;
  1864. }
  1865.  
  1866. void
  1867. WishCmdTableInit(cmdTablePtr, interpPtr, commands, clientData)
  1868.     Cmd_Table    *cmdTablePtr;
  1869.     Tcl_Interp    **interpPtr;
  1870.     CmdInfo    commands[];
  1871.     ClientData    clientData;
  1872. {
  1873.     CmdInfo    *cmd;
  1874.     int        i;
  1875.  
  1876.     *cmdTablePtr = Cmd_TableCreate();
  1877.     *interpPtr = Tcl_CreateInterp(); 
  1878.     for (cmd = commands; cmd->name != NULL; cmd++) {
  1879.     Tcl_CreateCommand(*interpPtr, cmd->name, cmd->proc, clientData,
  1880.         (void (*)()) NULL);
  1881.     }
  1882.     for (i = insertFirst; i <= insertLast; i++) {
  1883.     char    string[2];
  1884.  
  1885.     string[0] = i;
  1886.     string[1] = 0;
  1887.     Cmd_BindingCreate(*cmdTablePtr, string, "!@");
  1888.     }
  1889.  
  1890.     return;
  1891. }
  1892.  
  1893.  
  1894. /*
  1895.  *----------------------------------------------------------------------
  1896.  *
  1897.  * WishAddGroupBinding --
  1898.  *
  1899.  *    Add a command binding to a file group.
  1900.  *
  1901.  * Results:
  1902.  *    None.
  1903.  *
  1904.  * Side effects:
  1905.  *    New command binding added to given group.
  1906.  *
  1907.  *----------------------------------------------------------------------
  1908.  */
  1909. void
  1910. WishAddGroupBinding(grpPtr, button, command)
  1911.     WishGroup        *grpPtr;
  1912.     int            button;            /* button binding */
  1913.     char        *command;
  1914. {
  1915.     WishGroupBinding    *bPtr;
  1916.  
  1917.     for (bPtr = grpPtr->groupBindings; bPtr != NULL; bPtr = bPtr->nextPtr) {
  1918.     if (bPtr->button == button) {
  1919.         /* substitute command and return */
  1920.         if (bPtr->command != NULL) {
  1921.         free(bPtr->command);
  1922.         }
  1923.         bPtr->command = Util_Strcpy(NULL, command);
  1924.         return;
  1925.     }
  1926.     }
  1927.     /* Not there already */
  1928.     bPtr = (WishGroupBinding *) malloc(sizeof (WishGroupBinding));
  1929.     bPtr->button = button;
  1930.     bPtr->command = Util_Strcpy(NULL, command);
  1931.     bPtr->nextPtr = grpPtr->groupBindings;
  1932.     grpPtr->groupBindings = bPtr;
  1933.  
  1934.     return;
  1935. }
  1936.  
  1937. void
  1938. WishDeleteGroupBindings(grpPtr)
  1939.     WishGroup    *grpPtr;
  1940. {
  1941.     WishGroupBinding    *bPtr, *nPtr;
  1942.  
  1943.     for (bPtr = grpPtr->groupBindings; bPtr != NULL; ) {
  1944.     nPtr = bPtr->nextPtr;
  1945.     free(bPtr->command);
  1946.     free(bPtr);
  1947.     bPtr = nPtr;
  1948.     }
  1949.     grpPtr->groupBindings = NULL;
  1950.  
  1951.     return;
  1952. }
  1953.  
  1954. void
  1955. WishDeleteGroupBinding(grpPtr, button)
  1956.     WishGroup    *grpPtr;
  1957.     int        button;
  1958. {
  1959.     WishGroupBinding    *bPtr, *nPtr;
  1960.  
  1961.     if (grpPtr->groupBindings == NULL) {
  1962.     return;
  1963.     }
  1964.     if (grpPtr->groupBindings->button == button) {
  1965.     bPtr = grpPtr->groupBindings;
  1966.     grpPtr->groupBindings = grpPtr->groupBindings->nextPtr;
  1967.     free(bPtr->command);
  1968.     free(bPtr);
  1969.  
  1970.     return;
  1971.     }
  1972.     for (bPtr = grpPtr->groupBindings; bPtr->nextPtr != NULL;
  1973.         bPtr = bPtr->nextPtr ) {
  1974.     nPtr = bPtr->nextPtr;
  1975.     if (nPtr->button == button) {
  1976.         bPtr->nextPtr = nPtr->nextPtr;
  1977.         free(nPtr->command);
  1978.         free(nPtr);
  1979.         return;
  1980.     }
  1981.     }
  1982.  
  1983.     return;
  1984. }
  1985.  
  1986.  
  1987.  
  1988. char *
  1989. WishGetGroupBinding(grpPtr, button)
  1990.     WishGroup    *grpPtr;
  1991.     int        button;
  1992. {
  1993.     WishGroupBinding    *bPtr;
  1994.  
  1995.     for (bPtr = grpPtr->groupBindings; bPtr != NULL; bPtr = bPtr->nextPtr) {
  1996.     if (bPtr->button == button) {
  1997.         break;
  1998.     }
  1999.     }
  2000.     if (bPtr == NULL) {
  2001.     return NULL;
  2002.     }
  2003.     return bPtr->command;
  2004. }
  2005.  
  2006.  
  2007.  
  2008. /*
  2009.  *----------------------------------------------------------------------
  2010.  *
  2011.  * WishDoCmd --
  2012.  *
  2013.  *    Execute a given Tcl command in a given window, and display
  2014.  *    error information if the command doesn't complete successfully.
  2015.  *
  2016.  * Results:
  2017.  *    Returns the result code from the command: TCL_OK, etc.
  2018.  *
  2019.  * Side effects:
  2020.  *    Can be almost arbitrary, depending on the command.
  2021.  *
  2022.  *----------------------------------------------------------------------
  2023.  */
  2024. int
  2025. WishDoCmd(aWindow, command)
  2026.     WishWindow    *aWindow;
  2027.     char    *command;
  2028. {
  2029.     int        result;
  2030.  
  2031.     result = Tcl_Eval(aWindow->interp, command, 0, (char **) 0);
  2032.     if (result == TCL_OK) {
  2033. #ifdef NOTDEF
  2034.     if (*aWindow->interp->result != 0) {
  2035. /* this won't work with close command, interpreter and window structure gone! */
  2036.         /*
  2037.          * output message - should call routine that uses 1-line window
  2038.          * if possible.
  2039.          */
  2040.     }
  2041. #endif NOTDEF
  2042.     return result;
  2043.     }
  2044.  
  2045.     WishPrintTclError(aWindow);
  2046.  
  2047.     return result;
  2048. }
  2049.  
  2050.  
  2051. /*
  2052.  * Local procedure used to turn empty or "-" strings into NULLs.
  2053.  */
  2054. static char *
  2055. GetString(string)
  2056.     char *string;
  2057. {
  2058.     if ((string[0] == 0) || ((string[0] == '-') && (string[1] == 0))) {
  2059.     return NULL;
  2060.     }
  2061.     return string;
  2062. }
  2063.  
  2064. int
  2065. WishWhichButton(token)
  2066.     char    *token;
  2067. {
  2068.  
  2069.     if (strcmp(token, "Left") == 0 ||
  2070.         strcmp(token, "left") == 0) {
  2071.     return WISH_LEFT_BUTTON;
  2072.     }
  2073.     if (strcmp(token, "Middle") == 0 ||
  2074.         strcmp(token, "middle") == 0) {
  2075.     return WISH_MIDDLE_BUTTON;
  2076.     }
  2077.     if (strcmp(token, "Right") == 0 ||
  2078.         strcmp(token, "right") == 0) {
  2079.     return WISH_RIGHT_BUTTON;
  2080.     }
  2081.     if (strcmp(token, "Meta") == 0 ||
  2082.         strcmp(token, "meta") == 0) {
  2083.     return WISH_META_BUTTON;
  2084.     }
  2085.     if (strcmp(token, "Shift") == 0 ||
  2086.         strcmp(token, "shift") == 0) {
  2087.     return WISH_SHIFT_BUTTON;
  2088.     }
  2089.     return 0;
  2090. }
  2091.